﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using MathematicallySafe.HeadCounter.Data.Common;
using MathematicallySafe.HeadCounter.Data;
using MathematicallySafe.HeadCounter.Data.Interfaces;
using MathematicallySafe.HeadCounter.Data.Models;
using MathematicallySafe.HeadCounter.Dialogs;
using MathematicallySafe.HeadCounter.ViewModels;
using StructureMap;

namespace MathematicallySafe.HeadCounter.Services
{
    // todo this should be pushed down a tier but has all those headcount view model dependencies
    public class HeadCountRaidImporter
    {
        private readonly IRaidHistory RaidHistory = ObjectFactory.GetInstance<IRaidHistory>(); 
        private static readonly IGuild Guild = ObjectFactory.GetInstance<IGuild>(); 
        private static readonly ICalculationService CalculationService = ObjectFactory.GetInstance<ICalculationService>();

        private HeadCountRaidViewModel _headCountRaid;

        public HeadCountRaidImporter(HeadCountRaidViewModel headCountRaid)
        {
            _headCountRaid = headCountRaid;
        }

        public void ImportRaid()
        {
            var raid = new Raid()
                       {
                           Date = _headCountRaid.RaidDate,
                           Difficulty = _headCountRaid.Difficulty,
                           Zone = _headCountRaid.Zone,
                           Optional = _headCountRaid.Optional
                       };

            AddRaiders(raid);
            AddLoot(raid);

            CommitRaid(raid);
            Backup();
        }

        private void Backup()
        {
            var backupService = ObjectFactory.GetInstance<IBackupService>();

            backupService.ArchiveLocation = Constants.BackupDirectory;
            backupService.DataLocation = Constants.XmlDirectory;
            backupService.DataWildcard = @"/*.xml";

            backupService.Backup();
        }

        private void AddRaiders(Raid raid) 
        {
            var newRaiders = GetNewRaiders();
            if (newRaiders.Count() > 0)
            {
                AddNewRaidersToGuild(newRaiders);
            }

            _headCountRaid.Raiders.Where(r => !r.Ignore).ForEach(r => raid.AddRaider(r.Name, r.ActualMinutes));
            IgnoreLootForIgnoredRaiders();
        }

        private void IgnoreLootForIgnoredRaiders() 
        {
            IEnumerable<HeadCountLootViewModel> ignoredLoot = from r in _headCountRaid.Raiders
                                                              where r.Ignore
                                                              join l in _headCountRaid.Loot on r.Name equals l.LooterName
                                                              select l;

            ignoredLoot.ForEach(l => l.Ignore = true);
        }

        private void AddLoot(Raid raid) 
        {
            var loot = from l in _headCountRaid.Loot
                       where !l.Ignore
                       select l;

            foreach (var lootItem in loot)
            {
                var outcome = ParseOutcome(lootItem.LooterName);
                if (outcome != LootOutcome.Won) lootItem.LooterName = null;

                raid.AddLoot(lootItem.Id, lootItem.Time, lootItem.Name, lootItem.Boss, lootItem.LooterName, outcome);
            }
        }

        private void CommitRaid(Raid raid)
        {
            RaidHistory.AddRaid(raid);
            CalculationService.Process();

            // todo save the data on raid commit?
            //HeadCounterService.Save();
        }

        private IEnumerable<HeadCountRaiderViewModel> GetNewRaiders()
        {
            var newRaiders = from r in _headCountRaid.Raiders
                             where r.IsNewRaider && !r.Ignore
                             select r;

            return newRaiders;
        }

        private void AddNewRaidersToGuild(IEnumerable<HeadCountRaiderViewModel> newRaiders)
        {
            // First raid just add them all in without asking since all 25 will be new.
            var isFirstRaid = Guild.NumberOfRaiders == 0;

            foreach (HeadCountRaiderViewModel newRaider in newRaiders)
            {
                if (!isFirstRaid)
                {
                    var nrqd = new NewRaiderQueryDialog(newRaider.Name, newRaider.Class, newRaider.Race,
                                                        _headCountRaid.RaidDate);
                    nrqd.ShowDialog();

                    if (nrqd.Controller.IgnoreRaider)
                    {
                        newRaider.Ignore = true;
                    }
                }
                else
                {
                    Guild.AddRaider(newRaider.Name, newRaider.Class, newRaider.Race, _headCountRaid.RaidDate);
                }
            }
        }

        private LootOutcome ParseOutcome(string outcomeText)
        {
            if (outcomeText == "Bank") return LootOutcome.Banked;
            if (outcomeText == "Disenchanted") return LootOutcome.Disenchanted;
            if (outcomeText == "Offspec") return LootOutcome.Offspec;

            return LootOutcome.Won;
        }
    }
}